﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericGA.reader.regex
{
    /// <summary>
    /// A regular expression node for concatenating child regular expression nodes.
    /// </summary>
    /// <typeparam name="A">The type of the alphabet being matched.</typeparam>
    public class Concat<A> : Regex<A>
    {

        /// <summary>
        /// The operand regular expressions of the concatenation.
        /// </summary>
        private readonly Regex<A>[] operands;

        /// <summary>
        /// Constructor to concatenate an array of regular expressions.
        /// </summary>
        /// <param name="operands">Regular expressions to concatenate together.</param>
        public Concat(params Regex<A>[] operands)
        {
            this.operands = operands;
        }

        /// <summary>
        /// Generate a list which is matched by the regular expression based on a reader object. In this case, the concatenation of operand regular expressions.
        /// </summary>
        /// <param name="reader">Reader object to determine which list to generate. This regular expression doesn't consume any bits from the reader.</param>
        /// <returns>The concatenation of regular expressions.</returns>
        public override List<A> Generate(Reader reader)
        {
            List<A> result = new List<A>();
            foreach (Regex<A> operand in operands)
            {
                result.AddRange(operand.Generate(reader));
            }
            return result;
        }

        /// <summary>
        /// Enumerate every list which matches this regular expression.
        /// </summary>
        /// <returns>An iteration of the lists matched by the regular expression.</returns>
        public override IEnumerable<List<A>> Enumerate()
        {
            foreach (List<A> result in Enumerate(0))
            {
                yield return result;
            }
        }

        /// <summary>
        /// Helper function to recursively enumerate each list which is matched by this concatenation.
        /// </summary>
        /// <param name="operandIndex">The index of the operand which is currently being enumerated in the recursion process.</param>
        /// <returns>An enumeration of all the lists matched by concatenation of the operands whose index is greater than or equal to operandIndex.</returns>
        private IEnumerable<List<A>> Enumerate(int operandIndex)
        {
            //Recursive case: If more operands are left to process then return an enumeration of the lists which match the concatenation of the operands whose index is greater than or equal to operandIndex
            if (operandIndex < operands.Length)
            {
                //Enumerate all the lists matched by the operand at operandIndex and concatenate each one with each list matched by the concatenation of the operands whose index is greater than operandIndex
                foreach (List<A> head in operands[operandIndex].Enumerate())
                {
                    foreach (List<A> tail in Enumerate(operandIndex + 1))
                    {
                        List<A> result = new List<A>(head);
                        result.AddRange(tail);
                        yield return result;
                    }
                }
            }
            //Base case: Return empty list
            else
            {
                yield return new List<A>();
            }
        }

    }
}
